package database;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HibernateProductsDAO implements IProductsDAO
{
	final static Logger logger = LoggerFactory.getLogger(HibernateProductsDAO.class);
	final static String NEW_LINE = System.getProperty("line.separator");
	static private HibernateProductsDAO instance = null;
	
	static HibernateProductsDAO getInstance()
	{
		if (instance == null)
		{
			instance = new HibernateProductsDAO();
		}
		return instance;
	}
	
	private static final SessionFactory sessionFactory;
    static
    {
        try
        {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
        }
        catch (Throwable ex)
        {
            // Make sure you log the exception, as it might be swallowed
        	logger.info("Initial SessionFactory Creation Failed");
            throw new ExceptionInInitializerError(ex);
        }
    }
    
    public static SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }
	
    private HibernateProductsDAO()
	{}
    
    @Override
    public synchronized Product AddProduct(String productName, String productUnit, Double productQuantityUnit, String productImage)
	{
		Product product = new Product(productName, productUnit, productQuantityUnit, productImage);
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
            logger.info("Begin Add/Update Product");
            System.out.println("Begin Add/Update Product");
			transaction = session.beginTransaction();
			session.saveOrUpdate(product);
			transaction.commit();
            logger.info("Add/Update Product Done");
            System.out.println("Add/Update Product Done");
            logger.info("Product Details:" + NEW_LINE +  product.toString());
            System.out.println("Product Details:" + NEW_LINE +  product.toString());
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
		return product;
	}
    
    @Override
	public synchronized Product AddProduct(Product product)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Begin Add/Update Product");
            System.out.println("Begin Add/Update Product");
			transaction = session.beginTransaction();
			session.saveOrUpdate(product);
			transaction.commit();
			logger.info("Add/Update Product Done");
            System.out.println("Add/Update Product Done");
            logger.info("Product Details:" + NEW_LINE +  product.toString());
            System.out.println("Product Details:" + NEW_LINE +  product.toString());
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
		return product;
	}
    
    @Override
	public synchronized Supermarket AddSupermarket(String supermarketName, String supermarketBranch, Double supermarketLocationLatitude, Double supermarketLocationLongitude)
	{
		Supermarket supermarket = new Supermarket(supermarketName, supermarketBranch, supermarketLocationLatitude, supermarketLocationLongitude);
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Begin Add/Update Supermarket");
            System.out.println("Begin Add/Update Supermarket");
			transaction = session.beginTransaction();
			session.saveOrUpdate(supermarket);
			transaction.commit();
			logger.info("Add/Update Supermarket Done");
            System.out.println("Add/Update Supermarket Done");
            logger.info("Supermarket Details:" + NEW_LINE +  supermarket.toString());
            System.out.println("Supermarket Details:" + NEW_LINE +  supermarket.toString());
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
		return supermarket;
	}
	
    @Override
	public synchronized Supermarket AddSupermarket(Supermarket supermarket)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Begin Add/Update Supermarket");
            System.out.println("Begin Add/Update Supermarket");
			transaction = session.beginTransaction();
			session.saveOrUpdate(supermarket);
			transaction.commit();
			logger.info("Add/Update Supermarket Done");
            System.out.println("Add/Update Supermarket Done");
            logger.info("Supermarket Details:" + NEW_LINE +  supermarket.toString());
            System.out.println("Supermarket Details:" + NEW_LINE +  supermarket.toString());
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
		return supermarket;
	}
    
    @Override
	public synchronized ProductSupermarket AddProductToSupermarket(Supermarket supermarket, Product product, Double price)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		ProductSupermarket productSupermarket = new ProductSupermarket();
		try
		{
			logger.info("Begin Add/Update Product To Supermarket");
            System.out.println("Begin Add/Update Product To Supermarket");
			transaction = session.beginTransaction();
			productSupermarket.setProduct(product);
			productSupermarket.setSupermarket(supermarket);
			productSupermarket.setPrice(price);
			product.getProductSupermarkets().add(productSupermarket);
			session.saveOrUpdate(productSupermarket);
			transaction.commit();
			logger.info("Add/Update Product To Supermarket Done");
            System.out.println("Add/Update Product To Supermarket Done");
            logger.info("Product Supermarket Details:" + NEW_LINE +  productSupermarket.toString());
            System.out.println("Product Supermarket Details:" + NEW_LINE +  productSupermarket.toString());
            
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
		return productSupermarket;
	}

	@Override
	public synchronized Product FindProductById(Integer productId)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Get Product By Id");
            System.out.println("Get Product By Id");
            transaction = session.beginTransaction();
			Product product = (Product) session.createCriteria(Product.class)
							.setFetchMode("productSupermarkets", FetchMode.JOIN)
							.add(Restrictions.eq("productId", productId))
							.uniqueResult();
			transaction.commit();
			logger.info("Product Retrieved");
			logger.info("Product Retrieved");
            System.out.println("Product Retrieved");
			return product;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	@Override
	public synchronized Product FindProduct(String productName, String productUnit, Double productQuantityPerUnit)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Get Product By Name");
            System.out.println("Get Product By Name");
            transaction = session.beginTransaction();
			Product product = (Product) session.createCriteria(Product.class)
							.setFetchMode("productSupermarkets", FetchMode.JOIN)
							.add(Restrictions.eq("productName", productName))
							.add(Restrictions.eq("productUnit", productUnit))
							.add(Restrictions.eq("productQuantityPerUnit", productQuantityPerUnit))
							.uniqueResult();
			transaction.commit();
			logger.info("Product Retrieved");
			System.out.println("Product Retrieved");
			return product;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	@Override
	public synchronized List<Product> FindProductsByName(String productName)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Get Product By Name");
            System.out.println("Get Product By Name");
            transaction = session.beginTransaction();
			List<Product> products = session.createCriteria(Product.class)
									 .setFetchMode("productSupermarkets", FetchMode.JOIN)
									 .add(Restrictions.eq("productName", productName))
									 .list();
			transaction.commit();
			logger.info("Product Retrieved");
			System.out.println("Product Retrieved");
			return products;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	@Override
	public synchronized Supermarket FindSupermarketById(Integer supermarketId)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Get Supermarket By Id");
            System.out.println("Get Supermarket By Id");
            transaction = session.beginTransaction();
            Supermarket supermarket = (Supermarket) session.createCriteria(Supermarket.class)
            						.setFetchMode("productSupermarkets", FetchMode.JOIN)
            						.add(Restrictions.eq("supermarketId", supermarketId))
            						.uniqueResult();
			transaction.commit();
			logger.info("Supermarket Retrieved");
			System.out.println("Supermarket Retrieved");
			return supermarket;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	@Override
	public synchronized Supermarket FindSupermarket(String supermarketName, String supermarketBranch, Double supermarketLocationLatitude, Double supermarketLocationLongitude)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Get Supermarket By Name");
            System.out.println("Get Supermarket By Name");
            transaction = session.beginTransaction();
            Supermarket supermarket = (Supermarket) session.createCriteria(Supermarket.class)
            						  .setFetchMode("productSupermarkets", FetchMode.JOIN)
            						  .add(Restrictions.eq("supermarketName", supermarketName))
            						  .add(Restrictions.eq("supermarketBranch", supermarketBranch))
            						  .add(Restrictions.eq("supermarketLocationLatitude", supermarketLocationLatitude))
            						  .add(Restrictions.eq("supermarketLocationLongitude", supermarketLocationLongitude))
            						  .uniqueResult();
			transaction.commit();
			logger.info("Supermarket Retrieved");
			System.out.println("Supermarket Retrieved");
			return supermarket;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	   @Override
	   public synchronized ProductSupermarket FindProductSupermarket(Product product, Supermarket supermarket)
	    {
	        Session session = HibernateProductsDAO.getSessionFactory().openSession();
	        Transaction transaction = null;
	        try
	        {
	            logger.info("Get Product, Supermarket and Price");
	            System.out.println("Get Product, Supermarket and Price");
	            transaction = session.beginTransaction();
	            List<ProductSupermarket> productSupermarkets = session.createCriteria(ProductSupermarket.class)
	                                                           .list();
	            transaction.commit();
	            for (Iterator<ProductSupermarket> iterPS = productSupermarkets.iterator(); iterPS.hasNext();)
                {
                    ProductSupermarket productSupermarket = (ProductSupermarket) iterPS.next();
                    if (productSupermarket.getProduct().getProductId().equals(product.getProductId()) &&
                        productSupermarket.getSupermarket().getSupermarketId().equals(supermarket.getSupermarketId()))
                    {
                        logger.info("Supermarket Retrieved");
                        System.out.println("Supermarket Retrieved");
                        return productSupermarket;
                    }
                } 
	            return null;
	        }
	        catch (HibernateException e)
	        {
	            if (transaction != null && transaction.isActive())
	            {
	                try
	                {
	                    // Second try catch as the rollback could fail as well
	                    transaction.rollback();
	                }
	                catch (HibernateException e1)
	                {
	                    logger.info("Error Rolling Back Transaction");
	                }
	            }
	            // throw again the first exception
	             throw e;
	        }
	        finally
	        {
	            session.close();
	        }
	    }
	   
	@Override
	public synchronized List<Supermarket> FindSupermarketsByName(String supermarketName)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Get Supermarket By Name");
            System.out.println("Get Supermarket By Name");
            transaction = session.beginTransaction();
            List<Supermarket> supermarkets = session.createCriteria(Supermarket.class)
            								 .setFetchMode("productSupermarkets", FetchMode.JOIN)
            								 .add(Restrictions.eq("supermarketName", supermarketName))
            								 .list();
			transaction.commit();
			logger.info("Supermarket Retrieved");
			System.out.println("Supermarket Retrieved");
			return supermarkets;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	@Override
	public synchronized List<Product> FindAllProducts()
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
            logger.info("Get Products");
            System.out.println("Get Products");
            transaction = session.beginTransaction();
            Query query = session.createQuery("from Product");
            List<Product> products = query.list();
            transaction.commit();
            return products;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}

	@Override
	public synchronized List<Supermarket> FindAllSupermarkets()
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
            logger.info("Get Supermarkets");
            transaction = session.beginTransaction();
            Query query = session.createQuery("from Supermarket");
            List<Supermarket> supermarkets = query.list();
            transaction.commit();
            return supermarkets;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	@Override
	public synchronized Set<ProductSupermarket> FindAllProductSupermarkets(Product product)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Get ProductSupermarkets");
            transaction = session.beginTransaction();
			product = (Product) session.createCriteria(Product.class)
					.setFetchMode("productSupermarkets", FetchMode.JOIN)
					.add(Restrictions.eq("productId", product.getProductId()))
					.uniqueResult();
			Set<ProductSupermarket> productSupermarkets = product.getProductSupermarkets();
			transaction.commit();
			logger.info("ProductSupermarkets Retrieved");
			return productSupermarkets;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	@Override
	public synchronized Set<ProductSupermarket> FindAllProductSupermarkets(Supermarket supermarket)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Get ProductSupermarkets");
            transaction = session.beginTransaction();
            supermarket = (Supermarket) session.createCriteria(Supermarket.class)
						.setFetchMode("productSupermarkets", FetchMode.JOIN)
						.add(Restrictions.eq("supermarketId", supermarket.getSupermarketId()))
						.uniqueResult();
			Set<ProductSupermarket> productSupermarkets = supermarket.getProductSupermarkets();
			transaction.commit();
			logger.info("ProductSupermarkets Retrieved");
			return productSupermarkets;
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
	}
	
	@Override
	public synchronized boolean DeleteProduct(Product product)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Begin Delete Product");
            System.out.println("Begin Delete Product");
			transaction = session.beginTransaction();
			Product getProduct = (Product) session.load(Product.class, product.getProductId());
			session.delete(getProduct);
			transaction.commit();
			logger.info("Delete Product Done");
            System.out.println("Delete Product Done");
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
		return true;
	}
	
	@Override
	public synchronized boolean DeleteSupermarket(Supermarket supermarket)
	{
		Session session = HibernateProductsDAO.getSessionFactory().openSession();
		Transaction transaction = null;
		try
		{
			logger.info("Begin Delete Supermarket");
            System.out.println("Begin Delete Supermarket");
			transaction = session.beginTransaction();
			Supermarket getSupermarket = (Supermarket) session.load(Supermarket.class, supermarket.getSupermarketId());
			session.delete(getSupermarket);
			transaction.commit();
			logger.info("Delete Supermarket Done");
            System.out.println("Delete Supermarket Done");
		}
		catch (HibernateException e)
		{
			if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                	transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
		}
		finally
		{
			session.close();
		}
		return true;
	}
	
	@Override
	public synchronized boolean DeleteProductFromSupermarket(ProductSupermarket productSupermarket)
	{
	    Session session = HibernateProductsDAO.getSessionFactory().openSession();
        Transaction transaction = null;
        try
        {
            logger.info("Begin Delete Product from Supermarket");
            System.out.println("Begin Delete Product from Supermarket");
            transaction = session.beginTransaction();
            ProductSupermarket getProductSupermarket = (ProductSupermarket) session.load(ProductSupermarket.class, productSupermarket.getPk());
            session.delete(getProductSupermarket);
            transaction.commit();
            logger.info("Delete Supermarket Done");
            System.out.println("Delete Supermarket Done");
        }
        catch (HibernateException e)
        {
            if (transaction != null && transaction.isActive())
            {
                try
                {
                    // Second try catch as the rollback could fail as well
                    transaction.rollback();
                }
                catch (HibernateException e1)
                {
                    logger.info("Error Rolling Back Transaction");
                }
            }
            // throw again the first exception
             throw e;
        }
        finally
        {
            session.close();
        }
        return true;
	}
}